#ifndef __FG__EXCEPTION__H__
#define __FG__EXCEPTION__H__

#include <time.h>
#include <string>
#include <iostream>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <exception>
#include <time.h>
#include <stdio.h>
#include <iostream>
#include <utility>
#include <thread>
#include <chrono>
#include <future>
#include <functional>
#include <atomic>
#include "PluginModule/Platform.h"
#include "LogPlugin/easylogging++.h"

#if FG_PLATFORM != FG_PLATFORM_WIN
#include <unistd.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <signal.h>
#include <execinfo.h>
#include <setjmp.h>

#if FG_PLATFORM == FG_PLATFORM_LINUX
#include <sys/prctl.h>
#endif

#endif

#if FG_PLATFORM != FG_PLATFORM_WIN
namespace FragrantGrass
{
	class ExceptFrame
	{
	public:
		jmp_buf env;
		int flag;
		void clear()
		{
			flag = 0;
			bzero(env, sizeof(env));
		}
		bool isDef()
		{
			return flag;
		}
		ExceptFrame()
		{
			clear();
		}
	};

	class Exception
	{
	private:
		Exception() {}

		std::string _msg;

	public:
		static ExceptFrame& ExceptStack();

		Exception(const char* format, ...)
		{
			char buf[1024] = { 0 };
			va_list args;
			va_start(args, format);
			vsprintf(buf, format, args);
			_msg = buf;
			va_end(args);
		}

		Exception(const std::string& msg)
		{
			time_t tt = time(NULL);
			tm* t = localtime(&tt);
			char temp[128] = { 0 };
			sprintf(temp, "%d-%02d-%02d %02d:%02d:%02d\n",
				t->tm_year + 1900,
				t->tm_mon + 1,
				t->tm_mday,
				t->tm_hour,
				t->tm_min,
				t->tm_sec);

			_msg.append(temp);
			_msg.append(" Exception ");
			_msg.append(msg);
		}

		Exception(const std::string& msg, const char* file, const int line)
		{
			time_t tt = time(NULL);
			tm* t = localtime(&tt);
			char temp[128] = { 0 };
			sprintf(temp, "%d-%02d-%02d %02d:%02d:%02d\n",
				t->tm_year + 1900,
				t->tm_mon + 1,
				t->tm_mday,
				t->tm_hour,
				t->tm_min,
				t->tm_sec);

			_msg.append(temp);
			_msg.append(" Exception ");
			_msg.append(msg);
			_msg.append(file);
			_msg.append(std::to_string(line));
		}

		static void StackTrace(int sig)
		{
			LOG(FATAL) << "crash sig:" << sig;

			int size = 16;
			void* array[16];
			int stack_num = backtrace(array, size);
			char** stacktrace = backtrace_symbols(array, stack_num);
			for (int i = 0; i < stack_num; ++i)
			{
				//printf("%s\n", stacktrace[i]);
				LOG(FATAL) << stacktrace[i];
			}

			free(stacktrace);
		}

		static void CrashHandler(int sig)
		{
			printf("received signal %d !!!\n", sig);
			StackTrace(sig);
			siglongjmp(ExceptStack().env, 1);
		}
		char const* what() const noexcept { return _msg.c_str(); }
	};
}


#define FG_CRASH_TRY \
FragrantGrass::Exception::ExceptStack().flag = sigsetjmp(Exception::ExceptStack().env,1);\
if(!FragrantGrass::Exception::ExceptStack().isDef()) \
{ \
signal(SIGSEGV, FragrantGrass::Exception::CrashHandler); \
printf("start use TRY\n");
#define FG_CRASH_END_TRY \
}\
else\
{\
FragrantGrass::Exception::ExceptStack().clear();\
}\
printf("stop use TRY\n");

#endif
#endif//__FG__EXCEPTION__H__
